<?php
/*//模仿一个用户登录的需求
class Login{
    const LOING_USER_UNKNOWN = 1;
    const LOGIN_WRONG_PASS =2;
    const LOGIN_ACCESS = 3;
    private $status = array();

    public function handleLogin($user,$pass,$ip){
        switch(rand(1,3)){
            case 1:
                $this->setStatus(self::LOGIN_ACCESS,$user,$ip);
                $ret =  true;
                break;
            case 2:
                $this->setStatus(self::LOGIN_WRONG_PASS,$user,$ip);
                $ret =  true;
                break;
            case 3:
                $this->setStatus(self::LOING_USER_UNKNOWN,$user,$ip);
                $ret =  true;
                break;
        }
    }

    private function setStatus($status,$user,$ip){
        $this->status = array($status,$user,$ip);
    }

    public function getStatus(){
        return $this->status;
    }
}*/

//观察者模式
/*interface Observable{
    function attach(Observer $observer);
    function detach(Observer $observer);
    function notify();
}

class Login implements Observable{
    const LOING_USER_UNKNOWN = 1;
    const LOGIN_WRONG_PASS =2;
    const LOGIN_ACCESS = 3;
    private $status = array();
    private $observers = array() ;

    public function attach(Observer $observer)
    {
        $this->observers[] = $observer;
    }

    public function detach(Observer $observer)
    {
        $newObservers = array();
        foreach($this->observers as $ob){
            if($ob !== $observer){
                $newObservers = $ob;
            }
        }
        $this->observers = $newObservers;
    }

    public function notify()
    {
        foreach($this->observers as $ob){
            $ob->update($this);
        }
    }

    public function handleLogin($user,$pass,$ip){
        switch(rand(1,3)){
            case 1:
                $this->setStatus(self::LOGIN_ACCESS,$user,$ip);
                $ret =  true;
                break;
            case 2:
                $this->setStatus(self::LOGIN_WRONG_PASS,$user,$ip);
                $ret =  true;
                break;
            case 3:
                $this->setStatus(self::LOING_USER_UNKNOWN,$user,$ip);
                $ret =  true;
                break;
        }
        $this->notify();
        return $ret;
    }

    private function setStatus($status,$user,$ip){
        $this->status = array($status,$user,$ip);
    }

    public function getStatus(){
        return $this->status;
    }
}

interface Observer{
    function update(Observable $observable);
}

class SecurityMonitor implements Observer{
    function update(Observable $observable)
    {
        $status = $observable->getStatus();
        if($status[0] == Login::LOGIN_ACCESS){
            print '发邮件';
        }
    }
}

$login = new Login();
$login->attach(new SecurityMonitor());
$login->handleLogin(1,2,3);*/

//这个时候存在问题：必须要login类提供Observable的getStatus方法，但是observer是不知道这个方法的！
//解决方法一：在定义一个loginObservable实现Observable接口在loginObservable里面强制实现getStatus方法。
//第二种方法：继续保持Observable不变，更改observer

//使用第二种解决方案
/*interface Observable{
    function attach(Observer $observer);
    function detach(Observer $observer);
    function notify();
}

class Login implements Observable{
    const LOING_USER_UNKNOWN = 1;
    const LOGIN_WRONG_PASS =2;
    const LOGIN_ACCESS = 3;
    private $status = array();
    private $observers = array() ;

    public function attach(Observer $observer)
    {
        $this->observers[] = $observer;
    }

    public function detach(Observer $observer)
    {
        $newObservers = array();
        foreach($this->observers as $ob){
            if($ob !== $observer){
                $newObservers = $ob;
            }
        }
        $this->observers = $newObservers;
    }

    public function notify()
    {
        foreach($this->observers as $ob){
            $ob->update($this);
        }
    }

    public function handleLogin($user,$pass,$ip){
        switch(rand(1,3)){
            case 1:
                $this->setStatus(self::LOGIN_ACCESS,$user,$ip);
                $ret =  true;
                break;
            case 2:
                $this->setStatus(self::LOGIN_WRONG_PASS,$user,$ip);
                $ret =  true;
                break;
            case 3:
                $this->setStatus(self::LOING_USER_UNKNOWN,$user,$ip);
                $ret =  true;
                break;
        }
        $this->notify();
        return $ret;
    }

    private function setStatus($status,$user,$ip){
        $this->status = array($status,$user,$ip);
    }

    public function getStatus(){
        return $this->status;
    }
}

interface Observer{
    function update(Observable $observable);
}

abstract class LoginObserver implements Observer{
    private $login;
    function __construct(Login $login)
    {
        $this->login = $login;
        $login->attach($this);
    }
    function update(Observable $observable)
    {
        if($observable === $this->login){
            $this->login->doUpdate($observable);
        }
    }
    abstract function doUpdate(Login $login);
}

class SecurityMonitor extends LoginObserver{
    function doUpdate(Login $login)
    {
        $status = $login->getStatus();
        if($status[0] == Login::LOGIN_ACCESS){
            print '发邮件';
        }
    }
}*/


//使用SPL
class Login implements SplSubject{
    private $storage;
    const LOING_USER_UNKNOWN = 1;
    const LOGIN_WRONG_PASS =2;
    const LOGIN_ACCESS = 3;
    private $status = array();

    public function __construct()
    {
        $this->storage = new SplObjectStorage();
    }

    public function attach(SplObserver $observer)
    {
        $this->storage->attach($observer);
    }

    public function detach(SplObserver $observer)
    {
        $this->storage->detach($observer);
    }

    public function notify()
    {
        foreach($this->storage as $ob){
            $ob->update($this);
        }
    }

    public function handleLogin($user,$pass,$ip){
        switch(rand(1,3)){
            case 1:
                $this->setStatus(self::LOGIN_ACCESS,$user,$ip);
                $ret =  true;
                break;
            case 2:
                $this->setStatus(self::LOGIN_WRONG_PASS,$user,$ip);
                $ret =  true;
                break;
            case 3:
                $this->setStatus(self::LOING_USER_UNKNOWN,$user,$ip);
                $ret =  true;
                break;
        }
        $this->notify();
        return $ret;
    }

    private function setStatus($status,$user,$ip){
        $this->status = array($status,$user,$ip);
    }

    public function getStatus(){
        return $this->status;
    }
}

abstract class LoginObserver implements SplObserver{
    private $login;
    function __construct(Login $login)
    {
        $this->login = $login;
        $login->attach($this);
    }
    function update(SplSubject $subject)
    {
        if($subject === $this->login){
            $this->login->doUpdate($subject);
        }
    }
    abstract function doUpdate(Login $login);
}
